Skip to content

codex: capture proxy traffic to disk (parity with Claude mitm) - #61

Merged
Juliusolsson05 merged 1 commit into
mainfrom
feat/codex-proxy-capture
May 7, 2026
Merged

Juliusolsson05 merged 1 commit into
mainfrom
feat/codex-proxy-capture

Conversation

@Juliusolsson05

Copy link
Copy Markdown
Owner

Summary

Codex sessions now mirror their proxy traffic to disk in the same JSONL shape as the Claude mitm proxy. After this lands, debug bundles inspecting Codex traffic have access to the same forensic data we already had for Claude — request bodies, headers, pre-parsed shape — and a single bundle-inspection tool can read either provider without branching.

What changes per Codex session

A new on-disk JSONL under `~/.config/cc-shell/proxy////proxy-events.jsonl` captures every proxy event:

  • `kind: 'request'` events now include:
    • `headers` — filtered allowlist (`x-codex-installation-id`, `x-codex-window-id`, `x-codex-parent-thread-id`, `x-codex-turn-state`, `x-openai-subagent`, `user-agent`, `content-length`, `content-type`, `openai-beta`). Authorization structurally excluded.
    • `body_b64` — raw request body up to 2 MiB.
    • `request_shape` — pre-parsed `{ model, instructions_chars, input_items_count, tools_count, has_reasoning }`.
  • All other event kinds (`response-chunk`, `response-end`, `response-error`, `request-error`, `upgrade-rejected`, `server-error`) are mirrored as-is. `response-chunk`'s Buffer payload is base64-encoded inline so the file stays human-decodable.

Path layout is identical to the Claude proxy's so a single bundle reader can use one file path resolver for both.

Adapter tightening (bonus)

`CodexResponsesAdapter.onProxyEvent` previously filtered with `path.includes('/responses')` — a substring match that also accepted `/responses/compact` (the conversation compaction sidecar). Tightened to use the typed `endpoint === 'responses'` label the proxy already emits. Falls back to the legacy substring check when an older proxy doesn't send `endpoint`. Either way, `/responses/compact`, `/memories/trace_summarize`, and other sidecar endpoints no longer touch the active-flow attribution state machine.

Allowlist source-of-truth

Header allowlist derived by reading `vendor/codex-src/codex-rs/core/src/client.rs` — the headers that actually discriminate sidecar from main turn from subagent flows are:

  • `x-codex-installation-id` (state.installation_id, line 462-470)
  • `x-codex-window-id` (current_window_id(), line 569)
  • `x-codex-parent-thread-id` (only set on subagent calls, line 564-568)
  • `x-openai-subagent` (only set on subagent flows, line 552-560)
  • `x-codex-turn-state` (per-turn state, line 917)

Submodule

`packages/codex-headless` fix/feat-proxy-events-capture — commit `ffb6633`.

Verification

  • `npm run build` in `packages/codex-headless` ✅
  • `npm run build:app` in cc-shell ✅
  • After merge: start a Codex session, check that `~/.config/cc-shell/proxy/.../proxy-events.jsonl` is created and accumulates events with bodies + headers visible.

Companion PRs

🤖 Generated with Claude Code

…ents.jsonl

Brings Codex sessions up to parity with Claude's mitm proxy capture.
Bumps the codex-headless submodule to ffb6633 and wires the new
`eventsFile` option from cc-shell's CodexSession.

What's now captured per Codex session:
  - One on-disk JSONL under
      ~/.config/cc-shell/proxy/<project>/<session>/<run>/proxy-events.jsonl
    (path layout identical to Claude's mitm proxy capture).
  - Every proxy event mirrored to the file: request, response-chunk,
    response-end, response-error, request-error, upgrade-rejected,
    server-error, rejected.
  - For `kind: 'request'` events: filtered headers (x-codex-*,
    x-openai-subagent, user-agent, content-length, content-type,
    openai-beta) plus base64-encoded body (≤ 2 MiB) plus pre-parsed
    request_shape (model, instructions_chars, input_items_count,
    tools_count, has_reasoning).

Also picks up the adapter's endpoint-filter tightening from the
submodule: /responses/compact, /memories/trace_summarize, and other
sidecar endpoints stop touching the active-flow attribution state
machine.

cc-shell-side wiring (codexSession.ts):
  - allocateProxyEventsFile() mints a fresh run dir per proxy
    lifetime (a session restart gets its own dir, so multiple proxy
    incarnations don't interleave on the same file).
  - sessionKey naming mirrors claudeSession.ts exactly:
    `resume-<id>` / `shell-<ts>`.
  - Path components are sanitised so a malformed cwd or session id
    can't escape the proxy root via path traversal.

No breaking changes: existing CodexSession callers get the new
capture for free; testing harness callers that construct
ResponsesProxy directly still default to no on-disk mirror.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant